/**create by liuhua at 2019年5月20日 下午7:46:56**/
package com.star.truffle.gateway.route;

import static org.springframework.util.StringUtils.tokenizeToStringArray;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.gateway.discovery.DiscoveryLocatorProperties;
import org.springframework.cloud.gateway.filter.FilterDefinition;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder.Builder;
import org.springframework.cloud.netflix.eureka.EurekaDiscoveryClient.EurekaServiceInstance;
import org.springframework.http.HttpMethod;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.netflix.appinfo.InstanceInfo;
import com.star.truffle.gateway.eureka.EurekaRequestMetadata;
import com.star.truffle.gateway.filter.RewriteGatewayFilter;
import com.star.truffle.gateway.filter.StripPrefixGatewayFilter;

import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Flux;

@Slf4j
public class StarRouteLocator implements RouteLocator {

  private final DiscoveryClient discoveryClient;
  private final RouteLocatorBuilder builder;
  private final StripPrefixGatewayFilter stripPrefix;
  private final ObjectMapper objectMapper;
  
  public StarRouteLocator(RouteLocatorBuilder builder, DiscoveryClient discoveryClient, DiscoveryLocatorProperties properties, ObjectMapper objectMapper, StripPrefixGatewayFilter stripPrefix) {
    this.discoveryClient = discoveryClient;
    this.builder = builder;
    this.stripPrefix = stripPrefix;
    this.objectMapper = objectMapper;
  }

  public Flux<RouteDefinition> getRouteDefinitions() {
    discoveryClient.getServices().stream().forEach(serviceId -> {
      List<ServiceInstance> serviceInstances = discoveryClient.getInstances(serviceId);
      serviceInstances.stream().filter(instance -> null != instance.getMetadata() && ! instance.getMetadata().isEmpty())
          .map(serviceInstance1 -> {
            LocalDateTime appUpTime;
            String uptime = serviceInstance1.getMetadata().get("app-uptime");
            if(uptime == null) {
              appUpTime = LocalDateTime.now().minusYears(1);
            } else {
              appUpTime = LocalDateTime.parse(uptime, DateTimeFormatter.ISO_DATE_TIME);
            }
            System.out.println(appUpTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
            serviceInstance1.getMetadata().forEach((k, v) -> {
              System.out.println(k + "\t" + v);
            });
            return 1;
          })
//          .filter(stringEurekaRequestMetadataMap -> stringEurekaRequestMetadataMap.size() > 0)
          .collect(Collectors.toList());
    });
    String filters = "StripPrefix=2,RequestTime=true";
    String[] args = tokenizeToStringArray(filters, ",");
    RouteDefinition route1 = new RouteDefinition("star-jymgr=lb://star-jymgr,Path=/monk/**");
    route1.setFilters(Stream.of(args).map(arg -> new FilterDefinition(arg)).collect(Collectors.toList()));
    return Flux.fromIterable(Arrays.asList(route1));
  }

  @Override
  public Flux<Route> getRoutes() {
    List<EurekaRequestMetadata> datas = discoveryClient.getServices().stream().flatMap(serviceId -> {
      return discoveryClient.getInstances(serviceId).stream().filter(instance -> null != instance.getMetadata() && ! instance.getMetadata().isEmpty())
      .map(instance -> {
        EurekaServiceInstance esi = (EurekaServiceInstance) instance;
        InstanceInfo ii = esi.getInstanceInfo();
        String ipaddr = ii.getIPAddr();
        String homepage = ii.getHomePageUrl();
        String hostname = ii.getHostName();
        String homePageUrl = homepage.replace(hostname, ipaddr);
        String appName = ii.getAppName();
        String frameworkVersion = instance.getMetadata().get("framework-version");
        String uptime = instance.getMetadata().get("app-uptime");
        String apis = instance.getMetadata().get("app-apis");
        LocalDateTime appUpTime = StringUtils.isNotBlank(uptime) ? LocalDateTime.parse(uptime, DateTimeFormatter.ISO_DATE_TIME) : LocalDateTime.now().minusYears(1);
        if(StringUtils.isNotBlank(apis)) {
          try {
            List<EurekaRequestMetadata> requestMetadata = objectMapper.readValue(apis, new TypeReference<List<EurekaRequestMetadata>>() {});
            if(null != requestMetadata && ! requestMetadata.isEmpty()) {
              requestMetadata.forEach(data -> {
                data.setAppUptime(appUpTime);
                data.setFrameworkVersion(frameworkVersion);
                data.setHomePageUrl(homePageUrl);
                data.setAppName(appName);
                log.info(data.toString());
              });
            }
            return requestMetadata;
          }catch(Exception e) {
            e.printStackTrace();
            log.error(e.getMessage(), e);
          }
        }
        return null;
      }).filter(item -> null != item && ! item.isEmpty()).flatMap(items -> items.stream());
    }).collect(Collectors.toList());
    Builder bb = builder.routes();
    datas.stream().filter(data -> StringUtils.isBlank(data.getGateway())).forEach(data -> {
      String path = "/monk" + (data.getRealPath().startsWith("/") ? "" : "/") + data.getRealPath();
      bb.route(path + "_GET", r -> r.method(HttpMethod.GET).and().path(path).uri("lb://" + data.getAppName()).filters(stripPrefix))
//      .route(path + "_POST1", r -> r.method(HttpMethod.POST).and().header(HttpHeaders.CONTENT_TYPE, "((?!json).)*")
//          .and().readBody(String.class, readBody -> true).and().path(path).uri("lb://" + data.getAppName()).filters(stripPrefix))
//      .route(path + "_POST2", r -> r.method(HttpMethod.POST).and().header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
//          .and().readBody(Object.class, readBody -> true).and().path(path).uri("lb://" + data.getAppName()).filters(stripPrefix));
      .route(path + "_POST1", r -> r.method(HttpMethod.POST).and().header("token")
        .and().readBody(String.class, readBody -> true).and().path(path).uri("lb://" + data.getAppName()).filters(stripPrefix))
      .route(path + "_POST2", r -> r.method(HttpMethod.POST)
        .and().path(path).uri("lb://" + data.getAppName()).filters(stripPrefix));
    });
    
    datas.stream().filter(data -> StringUtils.isNotBlank(data.getGateway())).forEach(data -> {
      String path = data.getHomePageUrl() + data.getRealPath();
      if(data.getHomePageUrl().endsWith("/") && data.getRealPath().startsWith("/")) {
        path = data.getHomePageUrl() + data.getRealPath().substring(1);
      }
      String realPath = path;
      if(data.getMethods().contains(HttpMethod.GET.name())) {
        bb.route(data.getGateway() + "_GET", r -> r.method(HttpMethod.GET).and().path(data.getGateway()).uri(realPath).filters(new RewriteGatewayFilter(data.getGateway() + "_GET_1", realPath)));
      }
      if(data.getMethods().contains(HttpMethod.POST.name())) {
//        bb.route(data.getGateway() + "_POST1", r -> r.method(HttpMethod.POST).and().header(HttpHeaders.CONTENT_TYPE, "((?!json).)*")
//            .and().readBody(String.class, readBody -> true).and().path(data.getGateway()).uri(realPath).filters(new RewriteGatewayFilter(data.getGateway() + "_POST1_1", realPath)))
//        .route(data.getGateway() + "_POST2", r -> r.method(HttpMethod.POST).and().header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
//            .and().readBody(Object.class, readBody -> true).and().path(data.getGateway()).uri(realPath).filters(new RewriteGatewayFilter(data.getGateway() + "_POST2_1", realPath)));
        bb.route(data.getGateway() + "_POST1", r -> r.method(HttpMethod.POST).and().header("token")
          .and().readBody(String.class, readBody -> true).and().path(data.getGateway()).uri(realPath).filters(new RewriteGatewayFilter(data.getGateway() + "_POST1_1", realPath)))
        .route(data.getGateway() + "_POST2", r -> r.method(HttpMethod.POST)
          .and().path(data.getGateway()).uri(realPath).filters(new RewriteGatewayFilter(data.getGateway() + "_POST2_1", realPath)));
      }
     
    });
        
//        .route("route2", r -> r.method(HttpMethod.POST).and().header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE)
//            .and().readBody(String.class, readBody -> true).and().path("/monk/lh/**").uri("lb://star-jymgr").filters(stripPrefix))
//        
//        .route("route3", r -> r.method(HttpMethod.POST).and().header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
//            .and().readBody(Object.class, readBody -> true).and().path("/monk/lh/**").uri("lb://star-jymgr").filters(stripPrefix))
//        
//        .route("route4", r -> r.method(HttpMethod.GET).and().path("/abc").uri("lb://star-jymgr").filters(new RedirectGatewayFilter(HttpStatus.MOVED_PERMANENTLY, "http://local.cnhnkj.cn:8082/api/getAreaByAreaId?areaId=1")))
//        
//        .route("route5", r -> r.method(HttpMethod.POST).and().path("/a/b/c/d").uri("lb://star-jymgr").filters(new RewriteGatewayFilter("/a/b/c/d", "http://local.cnhnkj.cn:8082/api/getAreaByAreaId")));
    Flux<Route> flux = bb.build().getRoutes();
    return flux;
  }
}
